// breem.cpp : Defines the entry point for the console application.
//

#include "StdAfx.h"
#include "breemlib.h"
#include "Params.h"				//for parameter handling
#include "State.h"				//for CHighScore, need to unhack the highscore stuff
#if defined(SDL)
#include "SDLVideo.h"
#include "SDLSound.h"
#include "SDLInput.h"
#elif defined(GEKKO)
#include "system/wii/SDLSystem.h"
#elif defined(WIN32)
#include "system/win32/Win32System.h"
#endif
//#include "video.h"
//#include "input.h"
//#include "sound.h"
#include "version.h"
#include "ConfigFile.h"

#define MAX_ROMSETS	32

//name/version
char breemlib_name[] = APPNAME;
char breemlib_version[] = VERSION;
char breemlib_copyright[] = "Copyright 2007-2009 Dead_Body";

//emulator variables
CRomSet *RomSet = 0;								//pointer to romset
CMachine *Machine = 0;							//pointer to machine
CVideo *video = 0;								//pointers to system classes
CInput *input = 0;
CSound *sound = 0;

//timing variables
static u32 freq,ticks;
static u32 frametime,last;
static u32 frames;

//system variables
int quit = 0;										//quit emulation?
int running = 1;									//run emulator
u32 nolock = 0;									//do not lock fps
u16 SoundBuf[1024];								//sound buffer
//CSettings settings;								//emulator settings
//CConfiguration config;								//emulator config
int VideoBPP = 0;									//bpp for video (0 = window)
CRomSet *RomSets[MAX_ROMSETS];				//supported romsets
u32 *screen;

//path to store user data
char userpath[1024];

extern "C" void AddRomSet(CRomSet *r)
{
int n;
//char errorstr[64];

for(n=0;n<MAX_ROMSETS;n++)		//try to add romset to list
	{
	if(RomSets[n] == 0)			//found empty entry, add here
		{
		RomSets[n] = r;			//set pointer
		return;						//our work is done
		}
	}
for(n=0;RomSets[n];n++);		//count romsets
message("increase MAX_ROMSETS and recompile (%d found, %d max)",n,MAX_ROMSETS);
exit(-1);							//quit
}

extern "C" void message(char *m,...)		//print a message
{
char buffer[256];					//buffer to store vsprintf'd message in
va_list argptr;					//argument data

va_start(argptr,m);				//get data
vsprintf(buffer,m,argptr);		//print to buffer8
va_end(argptr);					//done!
printf(buffer);					//now display it
}

__inline void checkmessages()	//check for pending windows messages
{
/*
MSG msg;

while(PeekMessage(&msg,0,0,0,PM_REMOVE))	//see if a message is waiting
	{
	TranslateMessage(&msg);		//do secret microsoft stuff
	DispatchMessage(&msg);		//send it on its way
	}*/
	SDL_Event event;

	if(SDL_PollEvent(&event)) {
		switch(event.type) {
			case SDL_QUIT:
				quit++;
				break;
			case SDL_KEYDOWN:
				if(event.key.keysym.sym == SDLK_ESCAPE)
					quit++;
				break;
		}
	}

}

__inline void calcfps()
{
if(nolock == 0)
	{
	for(;;)							//lock framerate
		{
		ticks = SDL_GetTicks();
		if((ticks - last) >= frametime)
			break;
		checkmessages();			//process any waiting messages
		}
	}
ticks = SDL_GetTicks();
if((frames & 0x1f) == 0)		//show fps in title bar
	{
	u32 t = ticks - last;
	char fpsstr[128];

	if(t)
		{
		if(RomSet)
			sprintf(fpsstr,"%s - %s (%0.1f fps)",APPNAME,RomSet->GetName(),(float)freq / t);
		else
			sprintf(fpsstr,APPNAME);
		}
	SDL_WM_SetCaption(fpsstr,NULL);
	}
frames++;							//increment frame counter
last = ticks;
}

//returns folder for user to store app info in
int GetUserPath(char *userpath)
{
#if (defined(LINUX) || defined(OSX))
	char *homedir = getenv("HOME");

	if(homedir) {
		sprintf(curdir,"%s/.nesemu/",homedir);
		mkdir(curdir);
	}
#elif defined(WIN32)
	int i;
	TCHAR appdata[MAX_PATH];

	//get "application data" folder, and append our name
	SHGetSpecialFolderPath(0,appdata,CSIDL_APPDATA,TRUE);
	strcpy(userpath,appdata);
	strcat(userpath,"/"APPNAME);
	mkdir(userpath);
	//convert windows path to unix path
	for(i=0;userpath[i];i++) {
		if(userpath[i] == '\\')
			userpath[i] = '/';
	}
#elif defined(GEKKO)
	strcpy(userpath,"sd:/nesemu");
#else
	strcpy(userpath,argv0);
#endif
	printf("userpath = '%s'\n",userpath);
	return(0);
}

int GetRomSetIndex(CRomSet *r)	//get index of given romset in the romset array
{
int n;

for(n=0;RomSets[n] != 0;n++)		//search thru list
	{
	if(r == RomSets[n])				//found a match!
		return(n);						//return its index!
	}
return(-1);								//return error
}

int Init(CRomSet *r)
{
CHighScore highscore;

Kill();													//ensure old data is unloaded
screen = new unsigned long[r->ScreenW() * r->ScreenH()];
RomSet = r;												//system initialized ok, save romset pointer
Machine = RomSet->CreateMachine();				//create machine used by romset
frametime = freq / Machine->FPS();				//calculate frame interval
Machine->SetVideo(video);							//set machines internal video/input/sound pointers
Machine->SetInput(input);
Machine->SetSound(sound);
if(RomSet->Load(settings.romdir) == 0			//try to load the roms from disk
	&& Machine->Load(RomSet) == 0)				//if success, try to load the roms into machine
	{
	input->dip = settings.dip[GetRomSetIndex(r)];//set dip information
	Machine->Init();									//roms loaded into the machine, init machine
	Machine->Reset();									//reset machine
	}
else														//roms didnt load or machine didnt accept romset
	{
	delete Machine;									//destroy machine
	Machine = 0;										//zero the current machine pointer
	RomSet = 0;											//zero the current romset pointer
	message("error initializing romset %s\n",r->GetName());	//complain
	return(1);
	}
return(0);
}

void Kill()
{
if(Machine != 0)						//is machine even loaded?
	{
	Machine->SaveScores();			//save high scores
	//save nvram here!
	settings.dip[GetRomSetIndex(Machine->GetRomSet())] = input->dip;
	settings.Save();					//save settings
	delete Machine;					//destroy machine
	}
if(RomSet)								//romset should be loaded
	RomSet->Unload();					//unload loaded roms
if(screen)
	delete[] screen;
screen = 0;
Machine = 0;							//zero pointers
RomSet = 0;
}

//the main function needs a rewrite
#if defined(GEKKO)
int SDL_main(int argc, char* argv[])
#elif defined(_DEBUG) || defined(LINUX)
int main(int argc, char* argv[])
#else
int WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
#endif
{
int i,n,j;
u32 dd,nn;
dip_t *dip;

#if !defined(_DEBUG) && !defined(GEKKO) && !defined(LINUX)
int argc;
char **argv,*pstart,*pend,*str;
list_c argvlist;

pstart = lpCmdLine;
for(;;)
	{
	while(*pstart == ' ') //skip any spaces
		pstart++;
	if(strcmp(pstart,"") != 0) //is there another entry?
		{
		if(*pstart == '\"') //beginning quote, this is all one str
			pend = strchr(pstart,'\"');
		else if((pend = strchr(pstart,' ')) == 0)
			pend = lpCmdLine + strlen(lpCmdLine);
		str = new char[pend - pstart + 1];
		memset(str,0,pend - pstart + 1);
		strncpy(str,pstart,pend - pstart);
		pstart = pend;
		argvlist.add(str);
		}
	else
		break;
	}
argc = argvlist.num() + 1;
argv = new char*[argc];
argv[0] = new char[_MAX_PATH];
GetModuleFileName(GetModuleHandle(0),argv[0],_MAX_PATH);
for(i=1;i<argc;i++)
	argv[i] = (char*)argvlist.get(i - 1);
#endif

memset(RomSets,0,sizeof(CRomSet*) * MAX_ROMSETS);
AddRomSets();
ParameterInit_Arg(argv,argc);						//set our argument pointers
freq = 1000;
last = ticks = SDL_GetTicks();
frametime = freq / 60;
for(n=0;RomSets[n] != 0;n++)						//set default dip settings
	{
	for(nn=0,j=0;j<4;j++)
		{
		if((dip = RomSets[n]->GetDip(j)) == 0)
			break;
		for(dd=0,i=0;dip[i].Name;i++)
			dd |= dip[i].Default;
		nn |= dd << (j * 8);
		}
	settings.dip_default[n] = nn;
	}
settings.SetDefault();
if(CheckParameter("--setdefaults") == 0)			//user wants to reset settings, so dont load
	settings.Load();										//load settings
if(InitSystem(800,600,CheckParameter("--fullscreen")) != 0)
	{														//initialize system classes (vid/inp/snd)
	message("error initializing system\n");	//complain, hopefully the bad component explained
	return(1);											//return error
	}
if(CheckParameter("--rompath")) {
    strcpy(settings.romdir,GetParameterData("--rompath"));
}
if(CheckParameter("--romset"))									//if romset parameter specified
	{
	for(i=0;RomSets[i] != 0;i++)
		{
		if(strcmp(RomSets[i]->GetName(),GetParameterData("--romset")) == 0)
			{
			if(Init(RomSets[i]) != 0)								//try to create machine
				break;
			printf("trying to load %s\n",RomSet->GetName());
			video->SetSize(RomSet->ScreenW(),RomSet->ScreenH());
			if(Machine)
				{
				if(CheckParameter("--loadstate"))				//load save state
					{
					CState *s = new CState();						//create new state object

					if(s->Load(0,RomSet->GetName()) == 0)		//try to load state
						{
						Machine->LoadState(s);						//load state into machine
						s->Close();										//close state object
						}
					delete s;											//destroy state object
					}
				}
			break;
			}
		}
	}
if(CheckParameter("--stop") ||				//see if emulation should begin paused
	CheckParameter("--pause"))
	running = 0;									//clear running flag
if(CheckParameter("--run"))					//see if emulation should begin running (default)
	running = 1;									//set running flag
while(quit == 0)
	{
	nolock = 0;
//	if(GetAsyncKeyState('Q'))					//cheap fast forward hack
//		nolock = 1;
	nolock += ~settings.lockfps & 1;
	if(running && Machine)						//if running flag is set and a machine is loaded
		Machine->Frame();							//execute a machine frame
//	else
//		Sleep(100);								//else sleep for 0.1 seconds
	calcfps();									//show frames per second in title bar
	checkmessages();							//process messages
	}
Kill();												//destroy machine
KillSystem();							//destroy system classes
settings.Save();									//save settings
for(n=0;RomSets[n] != 0;n++)					//free romsets
	delete RomSets[n];
return(0);
}
